package util

import (
	"io"
	"io/ioutil"
	"os"
	"path"

	"chainmaker.org/chainmaker/smarteditor/logger"
)

var log = logger.Get()

//CloseAndLogError close file and print log if close failed
func CloseAndLogError(file io.Closer) {
	stackInfo := ReadParentStack()
	err := file.Close()
	if err != nil {
		log.Warnf("close file fialed. stack:%s, err:%v", stackInfo, err)
	}
}

//FileExists return true if file exists
func FileExists(filePath string) bool {
	if _, err := os.Stat(filePath); err == nil || os.IsExist(err) {
		return true
	}
	return false
}

// CopyDir copy a directory to another
func CopyDir(src string, dst string) error {
	var err error
	var fds []os.FileInfo
	var srcInfo os.FileInfo

	if srcInfo, err = os.Stat(src); err != nil {
		return err
	}

	if err = os.MkdirAll(dst, srcInfo.Mode()); err != nil {
		return err
	}

	if fds, err = ioutil.ReadDir(src); err != nil {
		return err
	}
	for _, fd := range fds {
		srcFilePath := path.Join(src, fd.Name())
		dstFilePath := path.Join(dst, fd.Name())

		if fd.IsDir() {
			if err = CopyDir(srcFilePath, dstFilePath); err != nil {
				return err
			}
		} else {
			if err = CopyFile(srcFilePath, dstFilePath); err != nil {
				return err
			}
		}
	}
	return nil
}

// CopyDirSkip 拷贝目录下的所有文件,除了指定的文件
func CopyDirSkip(src string, dst string, skipFiles map[string]struct{}) error {
	var err error
	var fds []os.FileInfo
	var srcInfo os.FileInfo

	if srcInfo, err = os.Stat(src); err != nil {
		return err
	}

	if err = os.MkdirAll(dst, srcInfo.Mode()); err != nil {
		return err
	}

	if fds, err = ioutil.ReadDir(src); err != nil {
		return err
	}
	for _, fd := range fds {
		tempFdName := fd.Name()
		if _, ok := skipFiles[tempFdName]; ok {
			continue
		}
		srcFilePath := path.Join(src, tempFdName)
		dstFilePath := path.Join(dst, tempFdName)
		if fd.IsDir() {
			if err = CopyDir(srcFilePath, dstFilePath); err != nil {
				return err
			}
		} else {
			if err = CopyFile(srcFilePath, dstFilePath); err != nil {
				return err
			}
		}
	}
	return nil
}

// CopyDirInclude 拷贝目录下的所有文件,仅拷贝include中指定的目录
func CopyDirInclude(src string, dst string, include map[string]struct{}) error {
	var err error
	var fds []os.FileInfo
	var srcInfo os.FileInfo

	if srcInfo, err = os.Stat(src); err != nil {
		return err
	}

	if err = os.MkdirAll(dst, srcInfo.Mode()); err != nil {
		return err
	}

	if fds, err = ioutil.ReadDir(src); err != nil {
		return err
	}
	for _, fd := range fds {
		tempFdName := fd.Name()
		if _, ok := include[tempFdName]; !ok {
			continue
		}
		srcFilePath := path.Join(src, tempFdName)
		dstFilePath := path.Join(dst, tempFdName)

		if fd.IsDir() {
			if err = CopyDir(srcFilePath, dstFilePath); err != nil {
				return err
			}
		} else {
			if err = CopyFile(srcFilePath, dstFilePath); err != nil {
				return err
			}
		}
	}
	return nil
}

// CopyFile copy a single file from src to dst
func CopyFile(src, dst string) error {
	var err error
	var srcFile *os.File
	var dstFile *os.File
	var srcInfo os.FileInfo

	if srcFile, err = os.Open(src); err != nil {
		return err
	}
	defer CloseAndLogError(srcFile)

	if dstFile, err = os.Create(dst); err != nil {
		return err
	}
	defer CloseAndLogError(dstFile)

	if _, err = io.Copy(dstFile, srcFile); err != nil {
		return err
	}
	if srcInfo, err = os.Stat(src); err != nil {
		return err
	}
	return os.Chmod(dst, srcInfo.Mode())
}
